package file

import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sync"
	"time"
)

// FileInfo 文件信息
type FileInfo struct {
	Name    string      `json:"name"`
	Path    string      `json:"path"`
	Size    int64       `json:"size"`
	Mode    os.FileMode `json:"mode"`
	ModTime time.Time   `json:"mod_time"`
	IsDir   bool        `json:"is_dir"`
	MD5     string      `json:"md5,omitempty"`
}

// FileTransfer 文件传输接口
type FileTransfer interface {
	// Upload 上传文件
	Upload(ctx context.Context, localPath, remotePath string) error
	// Download 下载文件
	Download(ctx context.Context, remotePath, localPath string) error
	// List 列出目录内容
	List(ctx context.Context, path string) ([]*FileInfo, error)
	// Remove 删除文件或目录
	Remove(ctx context.Context, path string) error
	// Mkdir 创建目录
	Mkdir(ctx context.Context, path string) error
	// Stat 获取文件信息
	Stat(ctx context.Context, path string) (*FileInfo, error)
}

// TransferProgress 传输进度
type TransferProgress struct {
	TotalSize     int64   `json:"total_size"`
	Transferred   int64   `json:"transferred"`
	Percent       float64 `json:"percent"`
	Speed         float64 `json:"speed"` // bytes/s
	ElapsedTime   float64 `json:"elapsed_time"`
	RemainingTime float64 `json:"remaining_time"`
}

// ProgressCallback 进度回调函数
type ProgressCallback func(progress *TransferProgress)

// ChunkTransfer 分块传输器
type ChunkTransfer struct {
	chunkSize int64
}

// NewChunkTransfer 创建新的分块传输器
func NewChunkTransfer(chunkSize int64) *ChunkTransfer {
	if chunkSize <= 0 {
		chunkSize = 64 * 1024 // 默认64KB
	}
	return &ChunkTransfer{
		chunkSize: chunkSize,
	}
}

// FileChunk 文件块
type FileChunk struct {
	ID       string `json:"id"`
	FileID   string `json:"file_id"`
	Index    int    `json:"index"`
	Data     []byte `json:"data"`
	Checksum string `json:"checksum"`
	EOF      bool   `json:"eof"`
}

// calculateMD5 计算文件MD5
func calculateMD5(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		return "", err
	}

	return hex.EncodeToString(hash.Sum(nil)), nil
}

// calculateMD5FromReader 从reader计算MD5
func calculateMD5FromReader(reader io.Reader) (string, error) {
	hash := md5.New()
	if _, err := io.Copy(hash, reader); err != nil {
		return "", err
	}
	return hex.EncodeToString(hash.Sum(nil)), nil
}

// LocalFileTransfer 本地文件传输实现
type LocalFileTransfer struct {
	rootPath     string
	progressChan chan *TransferProgress
	mutex        sync.RWMutex
}

// NewLocalFileTransfer 创建新的本地文件传输器
func NewLocalFileTransfer(rootPath string) *LocalFileTransfer {
	// 确保根路径存在
	os.MkdirAll(rootPath, 0755)

	return &LocalFileTransfer{
		rootPath: rootPath,
	}
}

// resolvePath 解析路径
func (lft *LocalFileTransfer) resolvePath(path string) string {
	// 防止路径遍历攻击
	cleanPath := filepath.Clean(path)
	absPath := filepath.Join(lft.rootPath, cleanPath)

	// 确保路径在根目录内
	if !filepath.HasPrefix(absPath, lft.rootPath) {
		return lft.rootPath
	}

	return absPath
}

// Upload 上传文件
func (lft *LocalFileTransfer) Upload(ctx context.Context, localPath, remotePath string) error {
	absRemotePath := lft.resolvePath(remotePath)

	// 打开源文件
	srcFile, err := os.Open(localPath)
	if err != nil {
		return fmt.Errorf("failed to open source file: %v", err)
	}
	defer srcFile.Close()

	// 创建目标目录
	remoteDir := filepath.Dir(absRemotePath)
	if err := os.MkdirAll(remoteDir, 0755); err != nil {
		return fmt.Errorf("failed to create directory: %v", err)
	}

	// 创建目标文件
	dstFile, err := os.Create(absRemotePath)
	if err != nil {
		return fmt.Errorf("failed to create destination file: %v", err)
	}
	defer dstFile.Close()

	// 获取源文件信息
	srcInfo, err := srcFile.Stat()
	if err != nil {
		return fmt.Errorf("failed to get source file info: %v", err)
	}

	// 复制文件内容
	totalSize := srcInfo.Size()
	var transferred int64
	buffer := make([]byte, 64*1024) // 64KB buffer

	startTime := time.Now()
	lastUpdateTime := startTime

	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}

		n, err := srcFile.Read(buffer)
		if err != nil && err != io.EOF {
			return fmt.Errorf("failed to read source file: %v", err)
		}

		if n == 0 {
			break
		}

		if _, err := dstFile.Write(buffer[:n]); err != nil {
			return fmt.Errorf("failed to write to destination file: %v", err)
		}

		transferred += int64(n)

		// 每秒更新一次进度
		now := time.Now()
		if now.Sub(lastUpdateTime) >= time.Second || transferred == totalSize {
			elapsed := now.Sub(startTime).Seconds()
			speed := float64(transferred) / elapsed
			percent := float64(transferred) / float64(totalSize) * 100

			var remainingTime float64
			if speed > 0 {
				remainingTime = float64(totalSize-transferred) / speed
			}

			progress := &TransferProgress{
				TotalSize:     totalSize,
				Transferred:   transferred,
				Percent:       percent,
				Speed:         speed,
				ElapsedTime:   elapsed,
				RemainingTime: remainingTime,
			}

			// 如果有进度通道，则发送进度
			lft.mutex.RLock()
			if lft.progressChan != nil {
				select {
				case lft.progressChan <- progress:
				default:
				}
			}
			lft.mutex.RUnlock()

			lastUpdateTime = now
		}
	}

	// 同步文件到磁盘
	if err := dstFile.Sync(); err != nil {
		return fmt.Errorf("failed to sync file: %v", err)
	}

	return nil
}

// Download 下载文件
func (lft *LocalFileTransfer) Download(ctx context.Context, remotePath, localPath string) error {
	absRemotePath := lft.resolvePath(remotePath)

	// 打开源文件
	srcFile, err := os.Open(absRemotePath)
	if err != nil {
		return fmt.Errorf("failed to open source file: %v", err)
	}
	defer srcFile.Close()

	// 创建本地目录
	localDir := filepath.Dir(localPath)
	if err := os.MkdirAll(localDir, 0755); err != nil {
		return fmt.Errorf("failed to create directory: %v", err)
	}

	// 创建本地文件
	dstFile, err := os.Create(localPath)
	if err != nil {
		return fmt.Errorf("failed to create destination file: %v", err)
	}
	defer dstFile.Close()

	// 获取源文件信息
	srcInfo, err := srcFile.Stat()
	if err != nil {
		return fmt.Errorf("failed to get source file info: %v", err)
	}

	// 复制文件内容
	totalSize := srcInfo.Size()
	var transferred int64
	buffer := make([]byte, 64*1024) // 64KB buffer

	startTime := time.Now()
	lastUpdateTime := startTime

	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}

		n, err := srcFile.Read(buffer)
		if err != nil && err != io.EOF {
			return fmt.Errorf("failed to read source file: %v", err)
		}

		if n == 0 {
			break
		}

		if _, err := dstFile.Write(buffer[:n]); err != nil {
			return fmt.Errorf("failed to write to destination file: %v", err)
		}

		transferred += int64(n)

		// 每秒更新一次进度
		now := time.Now()
		if now.Sub(lastUpdateTime) >= time.Second || transferred == totalSize {
			elapsed := now.Sub(startTime).Seconds()
			speed := float64(transferred) / elapsed
			percent := float64(transferred) / float64(totalSize) * 100

			var remainingTime float64
			if speed > 0 {
				remainingTime = float64(totalSize-transferred) / speed
			}

			progress := &TransferProgress{
				TotalSize:     totalSize,
				Transferred:   transferred,
				Percent:       percent,
				Speed:         speed,
				ElapsedTime:   elapsed,
				RemainingTime: remainingTime,
			}

			// 如果有进度通道，则发送进度
			lft.mutex.RLock()
			if lft.progressChan != nil {
				select {
				case lft.progressChan <- progress:
				default:
				}
			}
			lft.mutex.RUnlock()

			lastUpdateTime = now
		}
	}

	// 同步文件到磁盘
	if err := dstFile.Sync(); err != nil {
		return fmt.Errorf("failed to sync file: %v", err)
	}

	return nil
}

// List 列出目录内容
func (lft *LocalFileTransfer) List(ctx context.Context, path string) ([]*FileInfo, error) {
	absPath := lft.resolvePath(path)

	// 获取文件信息
	fileInfo, err := os.Stat(absPath)
	if err != nil {
		return nil, fmt.Errorf("failed to get file info: %v", err)
	}

	// 如果是文件而不是目录
	if !fileInfo.IsDir() {
		md5sum, _ := calculateMD5(absPath)
		return []*FileInfo{
			&FileInfo{
				Name:    fileInfo.Name(),
				Path:    path,
				Size:    fileInfo.Size(),
				Mode:    fileInfo.Mode(),
				ModTime: fileInfo.ModTime(),
				IsDir:   fileInfo.IsDir(),
				MD5:     md5sum,
			},
		}, nil
	}

	// 读取目录内容
	entries, err := os.ReadDir(absPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read directory: %v", err)
	}

	files := make([]*FileInfo, 0, len(entries))
	for _, entry := range entries {
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		default:
		}

		info, err := entry.Info()
		if err != nil {
			continue
		}

		// 构建相对路径
		relativePath := filepath.Join(path, info.Name())
		if relativePath == "." {
			relativePath = info.Name()
		}

		fileInfo := &FileInfo{
			Name:    info.Name(),
			Path:    relativePath,
			Size:    info.Size(),
			Mode:    info.Mode(),
			ModTime: info.ModTime(),
			IsDir:   info.IsDir(),
		}

		// 如果是文件，计算MD5
		if !info.IsDir() {
			md5sum, _ := calculateMD5(filepath.Join(absPath, info.Name()))
			fileInfo.MD5 = md5sum
		}

		files = append(files, fileInfo)
	}

	return files, nil
}

// Remove 删除文件或目录
func (lft *LocalFileTransfer) Remove(ctx context.Context, path string) error {
	absPath := lft.resolvePath(path)
	return os.RemoveAll(absPath)
}

// Mkdir 创建目录
func (lft *LocalFileTransfer) Mkdir(ctx context.Context, path string) error {
	absPath := lft.resolvePath(path)
	return os.MkdirAll(absPath, 0755)
}

// Stat 获取文件信息
func (lft *LocalFileTransfer) Stat(ctx context.Context, path string) (*FileInfo, error) {
	absPath := lft.resolvePath(path)

	info, err := os.Stat(absPath)
	if err != nil {
		return nil, fmt.Errorf("failed to get file info: %v", err)
	}

	// 构建相对路径
	relativePath := path
	if relativePath == "." || relativePath == "" {
		relativePath = info.Name()
	}

	fileInfo := &FileInfo{
		Name:    info.Name(),
		Path:    relativePath,
		Size:    info.Size(),
		Mode:    info.Mode(),
		ModTime: info.ModTime(),
		IsDir:   info.IsDir(),
	}

	// 如果是文件，计算MD5
	if !info.IsDir() {
		md5sum, _ := calculateMD5(absPath)
		fileInfo.MD5 = md5sum
	}

	return fileInfo, nil
}

// SetProgressChan 设置进度通道
func (lft *LocalFileTransfer) SetProgressChan(progressChan chan *TransferProgress) {
	lft.mutex.Lock()
	defer lft.mutex.Unlock()
	lft.progressChan = progressChan
}

// CloseProgressChan 关闭进度通道
func (lft *LocalFileTransfer) CloseProgressChan() {
	lft.mutex.Lock()
	defer lft.mutex.Unlock()
	if lft.progressChan != nil {
		close(lft.progressChan)
		lft.progressChan = nil
	}
}
